#include <errno.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/epoll.h>
#include <sys/poll.h>
#include <pthread.h>

#include "reactor.hpp"


#define RECV_MAX_SIZE 2048

#define MAX_EPOLL_EVENTS 512

int ITEMBLOCKS::block_num = 0;
int ITEM::recv_cnt = 0;
int ITEM::send_cnt = 0;

int init_server(int port)
{
	int listenfd;
	struct sockaddr_in server_addr;

	if(listenfd = socket(AF_INET, SOCK_STREAM, 0));
	if (listenfd < 0)
	{
		printf("create socket fail:%s(srrno: %d)", strerror(errno), errno);
	}

	server_addr.sin_port = htons(port);
	server_addr.sin_family = AF_INET;
	server_addr.sin_addr.s_addr = htonl(INADDR_ANY);

	if(bind(listenfd, (struct sockaddr *)& server_addr, sizeof(struct sockaddr)) < 0)
	{
		printf("bind socket fail:%s(srrno: %d)", strerror(errno), errno);
		return -1;
	}


	if(listen(listenfd, 10) < 0)
	{
		printf("listen socket fail:%s(srrno: %d)", strerror(errno), errno);
		return -1;
	}
	return listenfd;
}

int ITEM::item_conn(int listenfd)
{
    struct epoll_event ev;
    struct sockaddr_in client_addr;
    uint32_t len = sizeof(client_addr);
    printf("connecting \n");
	int connfd = accept(listenfd, (struct sockaddr *)&client_addr, &len);
    if(connfd < 0)
	{
		printf("accept socket fail:%s(srrno: %d)", strerror(errno), errno);
	}
	else
	{
		printf("PORT:%d\n", htons(client_addr.sin_port));
		printf("PORT:%s\n", inet_ntoa(client_addr.sin_addr));
	}

    return connfd;
}

int ITEM::item_read(int connfd)
{
    struct epoll_event ev;
    recv_len = recv(connfd, recv_buf, RECV_MAX_SIZE, 0);
	if(recv_len == 0)
	{
		printf("%d disconnect\n", connfd);
		close(connfd);
	}
	else if(recv_len < 0)
	{
		printf("recv socket fail:%s(srrno: %d)", strerror(errno), errno);
	}
	else
	{
		recv_buf[recv_len] = 0;
        ITEM::recv_cnt++;
        memcpy(send_buf, recv_buf, recv_len+1);
        send_len = recv_len;
	}

    return recv_len;
}

int ITEM::item_write(int connfd)
{
    struct epoll_event ev;

    int temp_len = send(connfd, send_buf, send_len, 0);
    
    ITEM::send_cnt++;
    std::cout << recv_cnt << ":" << send_cnt << std::endl;
    return send_len;
}

std::ostream& ITEM::operator<<(ITEM item)
{
    std::cout << item.event << std::endl;
    return std::cout;
}


ITEMBLOCKS::ITEMBLOCKS(int n) : size(n) {
    items = new ITEM[size];
    next = nullptr;
    block_num++;
}

ITEMBLOCKS::~ITEMBLOCKS() {
    delete [] items;
    block_num--;
}

ITEM* ITEMBLOCKS::operator[](int i) {
    ITEMBLOCKS *p = this;
    if(i > block_num)
        i = block_num;
    while(i-- && p)
    {
        p = p->next;
    }
    
    return p->items;
}

REACTOR::REACTOR(int listenfd, int epfd) : listenfd(listenfd), epfd(epfd)
{
    struct epoll_event ev;
    ev.events = EPOLLIN;
    ev.data.fd = listenfd;
    epoll_ctl(epfd, EPOLL_CTL_ADD, listenfd, &ev);

    itemblocks = new ITEMBLOCKS(1024);
}

REACTOR::~REACTOR()
{
    class ITEMBLOCKS* p1, *p2;
    p1 = itemblocks;
    p2 = p1->getnext();
    while(p2){
        delete p1;
        p1 = p2;
        p2 = p1->getnext();
    }
    delete p1;
}

void* REACTOR::reactor_thread(void *__this){
    int i,j;
    struct epoll_event ev;
    int connfd;

    REACTOR *_this = (REACTOR*) __this;
    while(1)
    {
        for(i=0;i<ITEMBLOCKS::block_num; i++)
        {
            for(j=0;j<1024;j++)
            {
                ITEM &item = (*_this->itemblocks)[i][j];

                if(item.event == EVENT_I)
                {
                    
                }
                else if(item.event == EVENT_A)
                {
                    connfd = item.item_conn(_this->listenfd);

                    if(connfd > 0)
                    {
                        item.event = EVENT_I;

                        ev.events = EPOLLIN;
                        ev.data.fd = connfd;
                        epoll_ctl(_this->epfd, EPOLL_CTL_ADD, connfd, &ev);
                        printf("%d : connect\n", connfd);
                    }
                    else 
                        printf("accept error :%d\n", connfd);
                }
                else if(item.event == EVENT_R)
                {

                    connfd = i*1024+j;
                    int n = item.item_read(connfd);
                    if(n == 0)
                    {
                        ev.data.fd = connfd;
		                epoll_ctl(_this->epfd, EPOLL_CTL_DEL, connfd, &ev);
                        item.event = EVENT_I;
                        memset(&item, 0, sizeof(ITEM));

                    }
                    else if (n > 0)
                    {
                        ev.data.fd = connfd;
                        ev.events = EPOLLOUT;
                        epoll_ctl(_this->epfd, EPOLL_CTL_MOD, connfd, &ev);
                        item.event = EVENT_W;
                        
                    }
                }else {

                    connfd = i*1024+j;
                    item.item_write(connfd);
                    ev.data.fd = connfd;
                    ev.events = EPOLLIN;
                    epoll_ctl(_this->epfd, EPOLL_CTL_MOD, connfd, &ev);
                    item.event = EVENT_R;
                }
            }
        }
    }
}

int REACTOR::run()
{
    struct epoll_event ev;
	struct epoll_event events[MAX_EPOLL_EVENTS];
    pthread_t pthread_id;

	ev.events = EPOLLIN;
	ev.data.fd = listenfd;
    int k,j;

    int t = pthread_create(&pthread_id, NULL, reactor_thread, (void *)this);

	epoll_ctl(epfd, EPOLL_CTL_ADD, listenfd, &ev);

	printf("================watting connect====================\n");
	while(1)
	{
		
		int conn_n = epoll_wait(epfd, events, MAX_EPOLL_EVENTS, -1);
		if(conn_n < 0)
		{
			continue;
		}

		
		for(int i=0; i<conn_n; i++)
		{
			int client_fd = events[i].data.fd;
			if (client_fd == listenfd)
			{
				k = client_fd/1024;
                j = client_fd%1024;
                ITEM &item = (*itemblocks)[k][j];
                item.event = EVENT_A;
			}
			else
			{
				int connfd = events[i].data.fd;

                k = connfd/1024;
                j = connfd%1024;
                ITEM &item = (*itemblocks)[k][j];
				if(events[i].events & EPOLLIN)
				{
					item.event = EVENT_R;
				}
				else if (events[i].events & EPOLLOUT)
				{
					item.event = EVENT_W;
				}
			}
		}
	}
}


int main(void)
{
	
	struct sockaddr_in client_addr;
	char buf[RECV_MAX_SIZE];
    int n;

	int listenfd = init_server(9998);
    int epfd = epoll_create(1);
    REACTOR reactor(listenfd, epfd);

    reactor.run();
    

	
}
